use std::collections::VecDeque;
use std::env;
use std::fmt::{Debug, Display, Error, Formatter};
use std::ops::Deref;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use std::error;

use futures::Stream;
use anyhow;
use indicatif::ProgressBar;
use reqwest::header::HeaderMap;
use serde_json::Value;
use sqlx::{Pool, QueryBuilder, Row, Sqlite};
use sqlx::query::Query;
use sqlx::sqlite::{SqlitePoolOptions, SqliteQueryResult};
use tracing::{error, info};
use fast_spider_derive::db_derive::CRUD;
use crate::db::{DATABASES, DatabaseSet};

use crate::http::Session;
use crate::models::bili::user::BiliUserModel;
use crate::models::default_count;
use crate::spider::bili::api::{subscribe_api, user_data_api};
use crate::spider::bili::BiliLoginCredential;
use crate::task::{MULTI_PROGRESS, PROGRESS_BAR_SPINNER_STYLE};

pub struct BiliUserSpider {
    session: Session<BiliLoginCredential>,
    cache: VecDeque<BiliUserModel>,
    tagid: i64,
    page_num: i64,
    page_size: i64,
}

impl Stream for BiliUserSpider {
    type Item = BiliUserModel;

    fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        // self.cache 不空
        if let Some(item) = self.cache.pop_front() {
            return Poll::Ready(Some(item));
        }
        // self.cache 为空
        let api = match subscribe_api(self.tagid, self.page_num, self.page_size) {
            Ok(url) => url,
            Err(err) => {
                error!("{}", err);
                return Poll::Ready(None);
            }
        };

        self.page_num += 1;
        // 请求 JSON 数据
        let resp = match self.session.request(api, reqwest::Method::GET, HeaderMap::new()) {
            Ok(resp) => resp,
            Err(err) => {
                error!("{}", err);
                return Poll::Ready(None);
            }
        };
        // 反序列化 JSON 数据
        let json = match resp.json::<Value>() {
            Ok(json) => json,
            Err(err) => {
                error!("{}", err);
                return Poll::Ready(None);
            }
        };
        // 获取 data 字段中的数据
        let data = match json.get("data") {
            None => return Poll::Ready(None),
            Some(data) => if let Some(data) = data.as_array() {
                data
            } else {
                return Poll::Ready(None);
            },
        };
        // 将 data 中的数据反序列化为 BiliUserModel
        let users = data.iter()
            .map(BiliUserModel::from)
            .collect::<Vec<BiliUserModel>>();
        users.into_iter().for_each(|x| self.cache.push_back(x));
        // 返回最新的数据
        Poll::Ready(self.cache.pop_front())
    }
}

impl Iterator for BiliUserSpider {
    type Item = BiliUserModel;

    fn next(&mut self) -> Option<Self::Item> {
        // self.cache 不空
        if let Some(item) = self.cache.pop_front() {
            return Some(item);
        }
        // self.cache 为空
        let api = match subscribe_api(self.tagid, self.page_num, self.page_size) {
            Ok(url) => url,
            Err(err) => {
                error!("{}", err);
                return None;
            }
        };

        self.page_num += 1;
        // 请求 JSON 数据
        let resp = match self.session.request(api, reqwest::Method::GET, HeaderMap::new()) {
            Ok(resp) => resp,
            Err(err) => {
                error!("{}", err);
                return None;
            }
        };
        // 反序列化 JSON 数据
        let json = match resp.json::<Value>() {
            Ok(json) => json,
            Err(err) => {
                error!("{}", err);
                return None;
            }
        };
        // 获取 data 字段中的数据
        let data = match json.get("data") {
            None => return None,
            Some(data) => if let Some(data) = data.as_array() {
                data
            } else {
                return None;
            },
        };
        // 将 data 中的数据反序列化为 BiliUserModel
        let users = data.iter()
            .map(BiliUserModel::from)
            .collect::<Vec<BiliUserModel>>();
        users.into_iter().for_each(|x| self.cache.push_back(x));
        // 返回最新的数据
        self.cache.pop_front()
    }
}

impl BiliUserSpider {
    pub fn new(tagid: i64) -> Self {
        let credential = BiliLoginCredential::new(
            &env::var("SESSDATA").unwrap_or("".to_owned()),
            &env::var("BILI_JCT").unwrap_or("".to_owned()),
            &env::var("DEDE_USER_ID").unwrap_or("".to_owned()),
            &env::var("BUVID3").unwrap_or("".to_owned()),
        );
        let session = Session::new(credential);

        Self {
            session,
            cache: VecDeque::new(),
            tagid,
            page_num: 1,
            page_size: 0,
        }
    }

    pub async fn update_user_online_info(&mut self, mid: i64, db: &Pool<Sqlite>) -> anyhow::Result<BiliUserModel> {
        let api = match user_data_api(&mid.to_string()) {
            Ok(url) => url,
            Err(err) => {
                error!("{}", err);
                return Err(err);
            }
        };
        // 请求 API
        info!("Request bili user info api");
        let resp = self.session.arequest(api, reqwest::Method::GET, HeaderMap::new()).await?;
        // 反序列化 JSON 数据
        info!("Deserialize json data to serde_json::Value");
        let resp = resp.json::<Value>().await?;
        let mut video_count = default_count();
        let mut audio_count = default_count();
        let mut album_count = default_count();
        let mut article_count = default_count();

        if let Some(value) = resp.get("data") {
            video_count = match value.get("video") {
                Some(v) => v.as_i64().unwrap_or(default_count()),
                None => default_count(),
            };
            audio_count = match value.get("audio") {
                Some(v) => v.as_i64().unwrap_or(default_count()),
                None => default_count(),
            };
            album_count = match value.get("album") {
                Some(v) => v.as_i64().unwrap_or(default_count()),
                None => default_count(),
            };
            article_count = match value.get("article") {
                Some(v) => v.as_i64().unwrap_or(default_count()),
                None => default_count(),
            };
        }
        info!("Update bili user info.");
        let mut user = match BiliUserModel::read_by_column(db, "mid", mid)
            .await
            .pop() {
            Some(u) => u,
            None => {
                error!("Unknown bili user '{}'", mid);
                return Ok(BiliUserModel {
                    mid,
                    uname: "".to_string(),
                    face: "".to_string(),
                    sign: "".to_string(),
                    video_count,
                    audio_count,
                    album_count,
                    article_count,
                    deleted: false,
                });
            }
        };
        user.video_count = video_count;
        user.audio_count = audio_count;
        user.album_count = album_count;
        user.article_count = article_count;
        info!("Save updated bili user into database.");
        user.update(db, "mid", user.mid).await;
        info!("Update bili user {}'s info complete.", user.uname);
        Ok(user)
    }

    pub async fn update_user_local_info(&mut self, db: &Pool<Sqlite>, mid: i64) -> anyhow::Result<SqliteQueryResult> {
        let mut qb = QueryBuilder::new("select count(ROWID) as ln from bili_album where poster_uid=");
        qb.push_bind(mid);

        let res = qb.build()
            .fetch_one(db)
            .await?;
        let mut user = BiliUserModel::read_by_column(db, "mid", mid).await;
        match user.pop() {
            Some(mut u) => {
                u.album_count = res.get::<i64, _>("ln");
                Ok(u.update(db, "mid", u.mid).await)
            },
            None => Ok(SqliteQueryResult::default())
        }
    }
}

pub async fn crawl_bili_user(credential: &BiliLoginCredential, tag_id: i64) -> SqliteQueryResult {
    // 获取数据库
    let guard = DATABASES[&DatabaseSet::DEFAULT].lock().await;
    let db_handler = guard.deref();
    // 从数据库获取已经抓取到的哔哩用户
    let local_bili_users = BiliUserModel::read_all(db_handler).await;
    let mut new_bili_user: Vec<BiliUserModel> = vec![];
    // 保存
    let spider = BiliUserSpider::new(tag_id);
    spider.for_each(|user| {
        if !local_bili_users.contains(&user) {
            new_bili_user.push(user);
        }
    });
    BiliUserModel::create_batch(db_handler, &new_bili_user, new_bili_user.len()).await.unwrap_or_else(|err| {
        error!("{}", err);
        SqliteQueryResult::default()
    })
}

#[cfg(test)]
mod tests {
    use fast_spider_derive::db_derive::CRUD;

    #[async_std::test]
    async fn test_update_user_local_info() {
        todo!()
    }
}